﻿@page "/datagrid/custom-binding"

@using Syncfusion.Blazor
@using Syncfusion.Blazor.Grids
@using Syncfusion.Blazor.Data
@using System.Collections
@using BlazorDemos
@using blazor_griddata
@inherits SampleBaseComponent;

<SampleDescription>
  <p>This sample demonstrates the usage of custom data binding in the DataGrid component.</p>
<p>The custom adaptor class has to be added as service using the <strong>AddScoped/AddSingleton</strong> method in the <strong>Startup.cs</strong>. You can inject your own services or DB context into custom adaptor class using <strong>Inject</strong> attribute on the particular property.</p>  
</SampleDescription>
<ActionDescription>
  <p>The custom data binding can be performed in the DataGrid component by providing the custom adaptor class
     and overriding the <strong>Read or ReadAsync</strong> method of the DataAdaptor abstract class.</p>
  <p>The custom adaptor class has to be added as service using the <strong>AddScoped/AddSingleton</strong> method in the <strong>Startup.cs</strong>. You can inject your own services or DB context into custom adaptor class using <strong>Inject</strong> attribute on the particular property.</p>
  <p>The CRUD operations for the custom bounded data in the DataGrid component can be implemented by overriding the following CRUD methods of the DataAdaptor abstract class,<br> </p>
      <ul>
         <li><strong>Insert/InsertAsync</strong> - Performs Insert operation.</li>
         <li><strong>Remove/RemoveAsync</strong> - Performs Remove operation.</li>
         <li><strong>Update/UpdateAsync</strong> - Performs Update operation.</li>
         <li><strong>BatchUpdate/BatchUpdateAsync</strong> - Performs BatchUpdate operation.</li>
      </ul>
   <p>In this demo, CRUD operations for the custom bounded data are performed using the methods of DataAdaptor abstract class.
      Similarly, you can perform Batch Update operations using the <strong>BatchUpdate/BatchUpdateAsync</strong> methods.</p>
    <p>More information on the custom data binding can be found in this <a target='_blank' href='https://blazor.syncfusion.com/documentation/grid/custom-binding/'>documentation section</a>.</p> 
</ActionDescription>

<div class="col-lg-12 control-section">
    <div class="content-wrapper">
        <div class="row">

            <SfGrid TValue="Order" AllowPaging="true" AllowGrouping="true" Toolbar="@(new string[] {"Add","Edit","Delete","Update","Cancel"})">
                <SfDataManager AdaptorInstance="@typeof(CustomAdaptor)" Adaptor="Adaptors.CustomAdaptor"></SfDataManager>
                <GridEditSettings AllowAdding="true" AllowEditing="true" AllowDeleting="true" Mode="EditMode.Normal"></GridEditSettings>
                <GridAggregates>
                    <GridAggregate>
                        <GridAggregateColumns>
                            <GridAggregateColumn Field=@nameof(Order.Freight) Type="AggregateType.Sum" Format="C2">
                                <FooterTemplate>
                                    @{
                                        var aggregate = (context as AggregateTemplateContext);
                                        <div>
                                            <p>Sum: @aggregate.Sum</p>
                                        </div>
                                    }
                                </FooterTemplate>
                            </GridAggregateColumn>
                        </GridAggregateColumns>
                    </GridAggregate>
                    <GridAggregate>
                        <GridAggregateColumns>
                            <GridAggregateColumn Field=@nameof(Order.Freight) Type="AggregateType.Average" Format="C2">
                                <FooterTemplate>
                                    @{
                                        var aggregate = (context as AggregateTemplateContext);
                                        <div>
                                            <p>Average: @aggregate.Average</p>
                                        </div>
                                    }
                                </FooterTemplate>
                            </GridAggregateColumn>
                        </GridAggregateColumns>
                    </GridAggregate>
                </GridAggregates>
                <GridColumns>
                    <GridColumn Field=@nameof(Order.OrderID) HeaderText="Order ID" IsPrimaryKey="true" TextAlign="@TextAlign.Right" ValidationRules="@(new ValidationRules{ Required=true, Number=true})" HeaderTextAlign="@TextAlign.Right" Width="140"></GridColumn>
                    <GridColumn Field=@nameof(Order.CustomerID) HeaderText="Customer Name" ValidationRules="@(new ValidationRules{ Required=true})" Width="150"></GridColumn>
                    <GridColumn Field=@nameof(Order.Freight) HeaderText="Freight" EditType="EditType.NumericEdit" Format="C2" ValidationRules="@(new ValidationRules{ Required=true, Range = new double[]{1, 1000}})" Width="140" TextAlign="@TextAlign.Right" HeaderTextAlign="@TextAlign.Right"></GridColumn>
                    <GridColumn Field=@nameof(Order.OrderDate) HeaderText="Order Date" EditType="EditType.DatePickerEdit" ValidationRules="@(new ValidationRules{ Required=true})" Format="d" TextAlign="TextAlign.Right" Type="ColumnType.Date" Width="160"></GridColumn>
                    <GridColumn Field=@nameof(Order.ShipCountry) HeaderText="Ship Country" EditType="EditType.DropDownEdit" Width="150"></GridColumn>
                </GridColumns>
            </SfGrid>
        </div>
    </div>
</div>

@code{
    //The custom adaptor class has to be added as service using AddScoped/AddSingleton method in the Startup.cs.
    //You can inject your own services or DB context into custom adaptor class using Inject attribute on the particular property.
    public class CustomAdaptor : DataAdaptor
    {

        public List<Order> Orders { get; set; } = Order.GetAllRecords();

        public override async Task<Object> ReadAsync(DataManagerRequest dataManagerRequest, string key = null)
        {
            IEnumerable GridData = Orders;
            IEnumerable DataSource = Orders;
            await Task.Delay(100); //To mimic asynchronous operation, we delayed this operation using Task.Delay
            if (dataManagerRequest.Sorted?.Count > 0) // perform Sorting
            {
                GridData = DataOperations.PerformSorting(GridData, dataManagerRequest.Sorted);
            }
            if (dataManagerRequest.Skip != 0)
            {
                GridData = DataOperations.PerformSkip(GridData, dataManagerRequest.Skip); //Paging
            }
            if (dataManagerRequest.Take != 0)
            {
                GridData = DataOperations.PerformTake(GridData, dataManagerRequest.Take);
            }
            IDictionary<string, object> aggregates = new Dictionary<string, object>();
            if (dataManagerRequest.Aggregates != null) // Aggregation
            {
                aggregates = DataUtil.PerformAggregation(DataSource, dataManagerRequest.Aggregates);
            }
            if (dataManagerRequest.Group != null && dataManagerRequest.Group.Any()) //Grouping
            {
                foreach (var group in dataManagerRequest.Group)
                {
                    GridData = DataUtil.Group<Order>(GridData, group, dataManagerRequest.Aggregates, 0, dataManagerRequest.GroupByFormatter);
                }
            }
            return dataManagerRequest.RequiresCounts ? new DataResult() { Result = GridData, Count = Orders.Count(), Aggregates = aggregates} : (object)GridData;
        }

        public override async Task<Object> InsertAsync(DataManager dataManager, object value, string key)
        {
            await Task.Delay(100); //To mimic asynchronous operation, we delayed this operation using Task.Delay
            Orders.Insert(0, value as Order);
            return value;
        }

        public override async Task<object> RemoveAsync(DataManager dataManager, object value, string keyField, string key)
        {
            await Task.Delay(100); //To mimic asynchronous operation, we delayed this operation using Task.Delay
            int data = (int)value;
            Orders.Remove(Orders.Where((Order) => Order.OrderID == data).FirstOrDefault());
            return value;
        }

        public override async Task<object> UpdateAsync(DataManager dataManager, object value, string keyField, string key)
        {
            await Task.Delay(100); //To mimic asynchronous operation, we delayed this operation using Task.Delay
            var val = (value as Order);
            var data = Orders.Where((Order) => Order.OrderID == val.OrderID).FirstOrDefault();
            if (data != null) {
                data.CustomerID = val.CustomerID;
                data.Freight = val.Freight;
                data.OrderDate = val.OrderDate;
                data.ShipCountry = val.ShipCountry;
            }
            return value;
        }
    }

    public class Order
    {
        public int OrderID { get; set; }
        public string CustomerID { get; set; }
        public DateTime OrderDate { get; set; }
        public double Freight { get; set; }
        public string ShipCountry { get; set; }

        public static List<Order> GetAllRecords() {
            List<Order> Orders = new List<Order>();

            Orders = Enumerable.Range(1, 70).Select(x => new Order()
            {
                OrderID = 1000 + x,
                CustomerID = (new string[] { "ALFKI", "ANANTR", "ANTON", "BLONP", "BOLID" })[new Random().Next(5)],
                Freight = 2.1 * x,
                OrderDate = DateTime.Now.AddDays(-x),
                ShipCountry = (new string[] { "France", "Germany", "Brazil", "Belgium", "Argentina", "Mexico"})[new Random().Next(6)]
            }).ToList();
            return Orders;
        }
    }
}